package fons.navigator.util.piccolo;

import java.awt.Color;
import java.awt.Font;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import fons.navigator.util.GeomUtils;

public class ContextMenu extends PNode {
	private static final long serialVersionUID = 1L;
	public static final Color MENU_ITEM_DEFAULT = new Color(0.95f, 0.95f, 0.95f);
	public static final Color MENU_ITEM_SELECTED = new Color(1.0f, 0.3f, 0.3f);
	public static final Font DEFAULT_FONT = new Font("Arial",Font.BOLD, 11);
	
	protected class Entry extends PNode {
		private static final long serialVersionUID = 1L;
		public Runnable callback;
		public ContextMenu subMenu;
		public String name;
				
		public ContextMenu getParentMenu () {
			return ContextMenu.this;
		}
		
		public ContextMenu getSubMenu () {
			return subMenu;
		}
		
		public boolean hasSubMenu () {
			return subMenu != null;
		}
	}
	
	private List<Entry> entries = Lists.newArrayList();
	private final PCamera camera;
	
	private PNode node;
	private PNode pickedNode;
		
	public ContextMenu (PCamera camera) {
		this.camera = camera;
	}
	
	private Entry selectedEntry;
	
	public Entry add (String name, Runnable callback) {
		final Entry entry = new Entry();
		entry.callback = callback;
		entry.name = name;
		
		final PText entryText = new PText(name);
		entryText.setFont(DEFAULT_FONT);
		
		final PPath background = new PPath(
				GeomUtils.growRectByRelativeSize(
						new Rectangle2D.Double(0, 0, entryText.getWidth(), entryText.getHeight()), 
						0.1, 0.1, 0.1, 0));
		
		background.setStroke(null);
		background.setPaint(MENU_ITEM_DEFAULT);
		entryText.addInputEventListener(new PBasicInputEventHandler() {
			@Override
			public void mouseEntered(PInputEvent event) {
				background.setPaint(MENU_ITEM_SELECTED);
				background.moveToFront();
				entryText.moveToFront();
				selectedEntry = entry;
			}
			
			@Override
			public void mouseExited(PInputEvent event) {
				background.setPaint(MENU_ITEM_DEFAULT);
				entryText.moveToBack();
				background.moveToBack();
				selectedEntry = null;
			}
		});
		entryText.setX(2);
		
		entry.addChild(background);
		entry.addChild(entryText);
		entries.add(entry);
		this.addChild(entry);
		
		final double maxHeight = this.getFullBoundsReference().getHeight();
		double height = -maxHeight;
		for (Entry item: entries) {
			height += item.getFullBoundsReference().getHeight() + 5;
			PNodes.setPosition(item, 0, height, true);
		}
		
		return entry;		
	}
	
	public void remove (Entry entry) {
		entries.remove(entry);
		this.removeChild(entry);
		
		final double maxHeight = this.getFullBoundsReference().getHeight();
		double height = -maxHeight;
		for (Entry item: entries) {
			height += item.getFullBoundsReference().getHeight() + 5;
			PNodes.setPosition(item, 0, height, true);
		}
	}
	
	public Entry add (String name) {
		return this.add(name, null);
	}
	
	public void setCallback (final String name, Runnable callback) {
		Iterables.find(entries, new Predicate<Entry>() {
			@Override
			public boolean apply(Entry arg0) {
				return arg0.name.equals(name);
			}
			
		}).callback = callback;
	}
	
	public void showAtView (Point2D pickedPoint, PNode pickedNode) {
		Point2D location = camera.viewToLocal(pickedPoint);
		camera.addChild(this);
		this.setOffset(location.getX(), location.getY());
		this.pickedNode = pickedNode;
	}
	
	public boolean isShown () {
		return this.getParent() != null;
	}
	
	public PNode getPickedNode () {
		return pickedNode;
	}
	
	public void remove () {
		if (selectedEntry != null && selectedEntry.callback != null) {
			selectedEntry.callback.run();
		}
		camera.removeChild(this);
	}
	
	public static void addTo (final PNode node, final ContextMenu menu) {
		menu.node = node;
		node.addInputEventListener(new PBasicInputEventHandler() {
			@Override
			public void mousePressed(PInputEvent event) {
				super.mousePressed(event);			
				
				if (event.isRightMouseButton()) {
					menu.showAtView(event.getPosition(), event.getPickedNode());
				}
			}
			
			@Override
			public void mouseReleased(PInputEvent event) {
				super.mouseReleased(event);
				
				if (event.isRightMouseButton()) {
					menu.remove();
				}
			}
		});
	}
	
	public PNode getNode () {
		return node;
	}

	public boolean hasEntry(String string) {
		for (Entry entry: this.entries) {
			if (entry.name.equals(string)) return true;
		}
		return false;
	}
}
